package org.study.ioc.core;

import org.study.ioc.bean.BeanDefinition;
import org.study.ioc.bean.PropertyArg;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

/**
 *
 *
 * @Author: Hukai
 * @Date: 2021/06/03/11:59
 * @Description: 实现一个读取xml容器,并且bean的描述解析好
 */
public class XmlApplicationContext extends BeanFactoryImp {
    //读取文件的名称
    private String fileName;
    public XmlApplicationContext(String fileName){
        this.fileName=fileName;
    }
    //提供一个初始化方法
    public void init() {
        try {
            this.loadFile();
        }catch (Exception e){
            e.getStackTrace();
        }

    }
    private void loadFile() throws ParserConfigurationException, IOException, SAXException, IllegalAccessException, InstantiationException {
        //获取配置文件所在的流
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
        //使用dom来解析配置文件,也可以使用别的框架来解析
        DocumentBuilderFactory documentBuilderFactory= DocumentBuilderFactory.newInstance();
        //获取DocumentBuilder
        DocumentBuilder documentBuilder=documentBuilderFactory.newDocumentBuilder();
        //传入流，来生成document对象
        Document doc  = documentBuilder.parse(inputStream);
        //获取到根节点
        Element root =doc.getDocumentElement();
        //获取根节点下的所有子节点
        NodeList nodes = root.getChildNodes();
        //存放beanDefinition的集合，每一个beanDefinition对应xml的一个配置的bean
        ArrayList<BeanDefinition> beanDefinitionArrayList = new ArrayList<BeanDefinition>();
        //遍历集合,  nodes里存放的<bean> 标签,如果还不太明白,这里可以看一下resources目录下的application.xml结构,解析的就是这个结构
        for(int i =0;i<nodes.getLength();i++){
           Node node =   nodes.item(i);
           //这里需要判断一下，要把node节点转换为Element标签,方便获取标签的属性和属性值
           if(node instanceof  Element){
               Element element =(Element)node;
               String id =element.getAttribute("id");
               String className =element.getAttribute("class");
               //通过反射创建BeanDefinition
               BeanDefinition beanDefinition =BeanDefinition.class.newInstance();
               beanDefinition.setName(id);
               beanDefinition.setClassName(className);
               //获取bean标签里面的property属性,
               NodeList propertyNodes = element.getElementsByTagName("property");
               //property有多个，应该是一个数组
               ArrayList<PropertyArg> propertyArgs = new ArrayList();
               for (int j=0;j<propertyNodes.getLength();j++){
                  Node propertyNode= propertyNodes.item(j);
                  if(propertyNode instanceof Element){
                      Element propertyElement = (Element) propertyNode;
                      PropertyArg propertyArg =PropertyArg.class.newInstance();
                      String name = propertyElement.getAttribute("name");
                      propertyArg.setName(name);
                      String value = propertyElement.getAttribute("value");
                      if (value!=null && !"".equals(value)) {
                          propertyArg.setValue(value);
                      }
                      String ref = propertyElement.getAttribute("ref");
                      if (ref!=null && !"".equals(ref)) {
                          propertyArg.setRef(ref);
                      }
                      //解析完一行property,存到propertyArgs集合中
                      propertyArgs.add(propertyArg);
                  }
               }
               //解析完bean的所有Property之后存放到beanDefinition的propertyArgs属性上
               beanDefinition.setPropertyArgs(propertyArgs);
               //解析完一个bean之后存到beanDefinitionArrayList中
               //这样，beanDefinitionArrayList的数据结构就和配置的xml结构是一致的
               beanDefinitionArrayList.add(beanDefinition);
           }
        }
        //如果能够从配置文件中解析出来beanDfinition，那么就把这些beanDefinition注册到容器中
        if(beanDefinitionArrayList!=null && beanDefinitionArrayList.size()>0){
            for (BeanDefinition beanDefinition:beanDefinitionArrayList){
                //由于我们父类的容器都是私有的,要在父类加一个方法能够注册进去
                super.regist(beanDefinition.getName(),beanDefinition);
            }
        }
    }
}
